package controllers

import java.util.Date
import java.util.UUID
import scala.collection.JavaConversions.asScalaSet
import scala.collection.mutable.StringBuilder
import play.api.libs.iteratee.Enumerator
import play.api.mvc.Action
import play.api.mvc.AnyContent
import play.api.mvc.BodyParser
import play.api.mvc.Codec
import play.api.mvc.Controller
import play.api.mvc.Request
import play.api.mvc.Result
import play.api.Logger
import transcoding.TranscodeProcessManager
import com.codahale.jerkson.Json._
import transcoding.MediaFileManager
import scala.collection.JavaConversions._
import java.io.FileNotFoundException

object Application extends Controller {

  implicit val myCustomCharset = Codec.javaSupported("UTF-8")
  LibraryManager

  private def Do(f: => Result): Action[play.api.mvc.AnyContent] = Do(parse.anyContent)(_ => f)
  private def Do[A](bp: BodyParser[A], f: => Result): Action[A] = Do(bp)(_ => f)
  private def Do[AnyContent](f: Request[play.api.mvc.AnyContent] => Result): Action[play.api.mvc.AnyContent] =
    Do(parse.anyContent)(f)
  private def Do[A](bp: BodyParser[A])(f: Request[A] => Result) = {
    Action(bp) {
      request =>
        var t: Date = null
        if (Logger.isDebugEnabled) {
          Logger.debug("Calling action for " + request.path)
          t = new Date()
        }
        try {
          f(request)
        } finally {
          if (Logger.isDebugEnabled) {
            Logger.debug("Time taken (milliseconds): %d".format(new Date().getTime() - t.getTime()))
          }
        }

    }
  }

  def dispatchTo(target: String): Action[AnyContent] = {
    try {
      val m = this.getClass().getMethod(target)
      val action = m invoke this
      action.asInstanceOf[Action[AnyContent]]
    } catch {
      case t => {
        val errorMessage = "Could not find %s".format(target)
        Logger.error(errorMessage, t)
        Do { (BadRequest(errorMessage)) }
      }
    }
  }

  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }

  def view_flash = Do {
    request =>
      val fileID = request.queryString.getOrElse("fileID", Seq()).fold("")((a, b) => a + b)
      val maxBitRate = request.queryString.getOrElse("maxBitRate", Seq()).fold("")((a, b) => a + b)
      LibraryManager.getMediaLibrary match {
        case Some(library) =>

          val videoLib = library.get(transcoding.Video)
          val mf = if (videoLib == null) null else videoLib.get(fileID.toInt)

          if (mf != null) {
            val title = new java.io.File(mf.mainMediaFile.filePath).getName

            Ok(views.html.flash_player(mf.mainMediaFile, Map("host" -> request.host, "playerID" -> "flash", "title" -> title, "fileID" -> fileID, "maxBitRate" -> maxBitRate)))
          } else
            NotFound("Could not find the video requested.")

        case None => BadRequest
      }
  }

  def listFiles = Do {
    import collection.JavaConversions._
    val html = new StringBuilder()
    LibraryManager.getMediaLibrary match {
      case Some(library) =>

        for (entry <- library.entrySet) {

          entry.getKey match {
            case transcoding.Other =>

            case _ =>
              html append entry.getKey.toString
              val innerHTML = new StringBuilder("")
              for (mediaEntry <- entry.getValue.entrySet) {
                innerHTML append <li><a href={ routes.Application.dispatchTo("view_flash?fileID=" + mediaEntry.getKey).url }>{ mediaEntry.getValue.mainMediaFile.filePath }</a></li>
              }
              html append ("<div><ul>%s</ul></div>".format(innerHTML))
          }

        }
      case None => html append (<h2>Library currently empty</h2>)
    }
    Ok(html.toString).as(HTML)
  }

  def ps = {
    import collection.JavaConversions._
    Do {
      Ok(TranscodeProcessManager.activeProcessesMap.entrySet.map(
        entry => {
          <p>{ entry.getKey }</p> +
            entry.getValue.map(
              tp => tp.getClass + " | " + tp.startedTime + " | " + tp.procInfo.toString + ", can cancel? " + tp.isCancellable).toString
        }).toString).as(HTML)
    }
  }

  def json_ps = {
    import collection.JavaConversions._
    Do {
      Ok(generate(TranscodeProcessManager.activeProcessesMap.entrySet
        .map(es => Map("filePath" -> es.getKey, "procInfos" -> es.getValue.map(tp =>
        Map("process" -> tp.getClass.getName, "info" -> tp.procInfo.info)))))).as(JSON)
    }
  }

  def json_list(query: String) = {
    Logger.debug("q=" + query)

    def toList(mediaTypeEntries: java.util.Map[Int, MediaFileManager]) = {
      mediaTypeEntries.values.map(
        mfm => Map("mediaID" -> mfm.mainMediaFile.hashCode, "title" -> mfm.fileName))
    }

    val res = Map("result" -> (LibraryManager.getMediaLibrary match {
      case None => Map("error" -> "Media Library still initialising!")
      case Some(library) => {
        val res: scala.collection.mutable.Map[transcoding.MediaType, Iterable[Any]] = scala.collection.mutable.Map.empty
        library.entrySet.foreach(entry => res += ((entry.getKey, toList(entry.getValue))))
        res
      }
    }))

    Do {
      Ok(generate(res)).as(JSON)
    }
  }

  def json_det(fileID: Int) = {
    Do {
      LibraryManager.getMediaFile(fileID) match {
        case None => NotFound(generate(Map("error" -> "ID: '%d' not found".format(fileID)))).as(JSON)
        case Some(mfm) => Ok(generate(Map("result" -> mfm))).as(JSON)
      }
    }
  }

  def json_stream(fileID: Int) = {
    Do {
      request =>
        val reqID = request.session.get("reqID").getOrElse(UUID.randomUUID.toString)

        LibraryManager.getMediaFile(fileID) match {
          case None => NotFound(generate(Map("error" -> "ID: '%d' not found".format(fileID)))).as(JSON)
          case Some(mfm) =>
            Logger.debug("Streaming file: %s".format(mfm.mainMediaFile.filePath))

            transcoding.TranscoderUtil(mfm, request, reqID) match {
              case None => NotFound
              case Some((stream, mf)) =>
                val dataContent: Enumerator[Array[Byte]] = Enumerator.fromStream(stream)
                Ok.stream(content = dataContent).withHeaders((CONTENT_TYPE -> mf.mimeType))
                  .withSession(("reqID", reqID))
            }
        }
    }
  }

  def json_addall = {

    Do{
      request => {
        try{
          LibraryManager.onboard()

          Ok(generate(Map("result" -> "Onboarding all videos in the media library"))).as(JSON)

        }
        catch{
          case exc => Ok(generate(Map("error" -> exc.getMessage))).as(JSON)
        }
      }
    }
  }

  def json_add = {
    Do{
      request => {
        try{
          val files = request.queryString("filePaths").map(
          path => {
            val file = new java.io.File(path)
            if(!file.exists)
              throw new FileNotFoundException(file.getAbsolutePath)

            file
          })

        LibraryManager.onboard(files)

        Ok(generate(Map("result" -> ("Adding %s to media library" format files)))).as(JSON)

        }
        catch{
          case exc => Ok(generate(Map("error" -> exc.getMessage))).as(JSON)
        }
      }
    }
  }

  def preProcess = {
    Do {
      request =>
        Logger.debug(request.queryString.toString)

        val fileParam = request.queryString.getOrElse("fileID", List())
        if (fileParam.eq(""))
          BadRequest("Media not found")
        else {
          val fileID = fileParam.fold("")((a, b) => a ++ b).toInt

          LibraryManager.getMediaFile(fileID) match {
            case None => NotFound

            case Some(mfm) => TranscodeProcessManager.startInitialProcess(mfm); Ok("Working...")
          }
        }
    }
  }

  def stream = {
    Do {
      request =>
        Logger.debug(request.queryString.toString)
        Logger.debug(request.session.toString)

        val fileParam = request.queryString.getOrElse("fileID", List())
        if (fileParam.eq(""))
          BadRequest("Media not found")
        else {
          val fileID = fileParam.fold("")((a, b) => a ++ b).toInt
          val reqID = request.session.get("reqID").getOrElse(UUID.randomUUID.toString)

          LibraryManager.getMediaFile(fileID) match {
            case None => NotFound

            case Some(mfm) =>
              Logger.debug("Streaming file: %s".format(mfm.mainMediaFile.filePath))

              transcoding.TranscoderUtil(mfm, request, reqID) match {
                case None => NotFound
                case Some((stream, mf)) =>
                  val dataContent: Enumerator[Array[Byte]] = Enumerator.fromStream(stream)
                  Ok.stream(content = dataContent).withHeaders((CONTENT_TYPE -> mf.mimeType))
                    .withSession(("reqID", reqID))
              }

          }
        }
    }
  }
}
